//
//  ViewController.m
//  socket
//
//  Created by Song on 2018/5/25.
//  Copyright © 2018年 Song. All rights reserved.
//

/*
 socket使用步骤:
     1.创建客户端Socket.
     2.连接到服务器Socket.
     3.客户端Socket发送数据到服务器Socket.
     4.客户端Socket接收服务器返回的数据.
     5.关闭客户端Socket.
 */

#import "ViewController.h"

#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>

#import <netdb.h> // 获取ip地址中的 hostent

#import "GCDAsyncSocket.h"

// 1.继承GCDAsyncSocketDelegate协议.
@interface ViewController () <GCDAsyncSocketDelegate>

// 2.声明属性 客户端socket
@property (nonatomic,strong) GCDAsyncSocket *clientSocket2;
@property (nonatomic,assign) BOOL connected;

// 计时器
@property (nonatomic, strong) NSTimer *connectTimer;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // iOS自带socket写法
//    [self iOSSelf];
    
    // CocoaAsyncSocket 写法
    [self cocoaAsyncSocket];
}

-(void)cocoaAsyncSocket {
    
    if (!self.connected) {
        NSString *host = @"119.75.217.109";
        int port = 80;
        // 3.创建socket对象 并指定代理对象为self,     ??? 代理队列必须为主队列 ???
        self.clientSocket2 = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
//        self.clientSocket2 = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        
        // 链接
        NSError *error = nil;
        [self.clientSocket2 connectToHost:host onPort:port error:&error];
        
        if (error) {
            NSLog(@"%@",error);
        }
//        else {
//            NSLog(@"连接成功====");
//        }
        
    } else {
        NSLog(@"与服务器链接已经建立");
    }
}

// 发送数据
-(void)sendMessage {
    // 发送数据
    NSString *sendMsg = @"GET / HTTP/1.1\r\n"
    "Host:www.baidu.com\r\n"
    "User-Agent: iphone\r\n"
    "Connection: close\r\n\r\n";
    NSData *data = [sendMsg dataUsingEncoding:NSUTF8StringEncoding];
    // withTimeout -1 : 无穷大,一直等
    // tag : 消息标记
    [self.clientSocket2 writeData:data withTimeout:-1 tag:0];
}

#pragma mark - socket的代理
#pragma mark 连接成功
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
    NSLog(@"链接成功：%s",__func__);
    // 连接成功后，开始发送数据
    [self sendMessage];
}
#pragma mark 断开连接
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    if (err) {
        NSLog(@"连接失败,%@",err);
    }else {
        NSLog(@"正常断开,%@",err);
    }
}
#pragma mark 数据发送成功
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
    NSLog(@"数据发送成功：%s",__func__);
    
    // 发送完数据手动读取，读取服务器端的数据，-1不设置超时
    [sock readDataWithTimeout:-1 tag:tag];
}
#pragma mark 读取数据
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    NSString *receiverStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"读取数据：%s %@",__func__,receiverStr);
    // 读取到服务器数据值后,能再次读取
    [self.clientSocket2 readDataWithTimeout:- 1 tag:0];
}


-(void)iOSSelf {
    /**
     1.创建客户端socket
     
     AF_INET  - domain,协议域/协议簇，AF_INET（IPV4的网络开发）
     SOCK_STREAM  - type,Socket类型，SOCK_STREAM(TCP)/SOCK_DGRAM(UDP，报文)
     0  - protocol,IPPROTO_TCP，协议，如果输入0，可以根据第二个参数，自动选择协议
     return  - int类型,如果 > 0 就表示创建客户端Socket成功,返回socket
     */
    int clientSocket = socket(AF_INET, SOCK_STREAM, 0); // 客户端socket
    if (clientSocket > 0) {
        NSLog(@"创建客户端socket成功 - %zd",clientSocket);
    }
    
    /*
     2.客户端socket连接到服务器socket
     
     __uint8_t	sin_len;
     sa_family_t	sin_family;
     in_port_t	sin_port;
     struct	in_addr sin_addr;
     char		sin_zero[8];
     
     */
    struct sockaddr_in addr;
    addr.sin_family = AF_INET; // 地址家族，通常大多用的是都是AF_INET,代表TCP/IP协议族。
    addr.sin_port = htons(80); // 是要设置端口 存储端口号（使用网络字节顺序）
    //    addr.sin_addr.s_addr = inet_addr("119.75.217.109"); // 指向的IP地址 存储IP地址，使用in_addr这个数据结构
    
    // 上一句注释掉的代码，也可以按如下写法：
    // 获取ip地址
    NSString *host = @"119.75.217.109";
    struct hostent *remoteHostEnt = gethostbyname([host UTF8String]);
    if (remoteHostEnt == NULL) {
        
        NSLog(@"无法解析服务器主机名");
        return;
    }else {
        NSLog(@"host 可以解析");
    }
    struct in_addr *remoteInAddr = (struct in_addr *)remoteHostEnt->h_addr_list[0];
    addr.sin_addr = *remoteInAddr;
    
    /**
     clientSocket 客户端socket
     (const struct sockaddr *)&addr 指向数据结构sockaddr的指针，其中包括目的端口和IP地址，服务器的"结构体"地址
     sizeof(addr) 结构体数据长度
     return 0 成功/其他 错误代号，(不是非0即真)
     */
    int isConnected = connect(clientSocket, (const struct sockaddr *)&addr, sizeof(addr));
    if (isConnected == 0) {
        NSLog(@"链接服务器成功");
    } else {
        NSLog(@"连接服务器失败！！！");
    }
    
    /*
     3.客户端socket向服务器socket发送请求
     */
    NSString *sendMsg = @"GET / HTTP/1.1\r\n"
                         "Host:www.baidu.com\r\n"
                         "User-Agent: iphone\r\n"
                         "Connection: close\r\n\r\n";
    /**
     参数：
     clientSocket 客户端socket
     sendMsg.UTF8String 发送内容地址
     strlen(sendMsg.UTF8String) 发送内容长度
     0 发送方式标志，一般为0
     
     return 如果成功，则返回发送的字节数，失败则返回SOCKET_ERROR  -1
     */
    ssize_t sendCount = send(clientSocket, sendMsg.UTF8String, strlen(sendMsg.UTF8String), 0);
    NSLog(@"发送字符数：%ld",sendCount);
    
    
    // 4.客户端socket接收服务器socket发送的数据（响应）
    // 创建接收服务器发送的数据的容器/缓冲区，并且制定了容量
    uint8_t buffer[1024];
    // 需要创建一个容器
    NSMutableData *dataM = [NSMutableData data];
    // 循环的接受服务器发送的数据
    ssize_t recvCount = -1;
    while (recvCount != 0) {
        
        /**
         clientSocket 客户端socket
         buffer 接收内容 缓冲区 地址
         sizeof(buffer) 接收内容 缓存区 长度
         0 接收方式，0表示阻塞，必须等待服务器返回数据
         return 如果成功，返回读入的字节数
         */
        recvCount = recv(clientSocket, buffer, sizeof(buffer), 0);
        // 提示 : 服务器发送给客户端数据时,是一点一点发送的
        // 提示 : 当服务器把数据都发送完了以后,再次发送时,只发送0字节,
        NSLog(@"接受的内容数：%ld",recvCount);
        [dataM appendBytes:buffer length:recvCount];
    }
    NSString *html = [[NSString alloc] initWithData:dataM encoding:NSUTF8StringEncoding];
    NSLog(@"%@",html);
    
    // 5.关闭socket
    close(clientSocket);
}
@end
